package gappengko;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;


import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.EntityNotFoundException;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.PreparedQuery;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.Query.FilterOperator;
import com.google.appengine.api.datastore.Text;

/** An abstraction on top of the Google Java Datastore.
 * 
 * The query model is simpler: it takes either a map<String,String> or a string in JSON
 * format {"name":"joe","date","june"}.
 * All conditions must be satisfied.
 */
public class SimpleDatastore {
  
  private String _kind = "SimpleDatastoreObject";
  
  public SimpleDatastore() {};

  public SimpleDatastore(String kind) {
    _kind = kind;
  }

	/** A shortcut to the Google Datastore service */
	private final DatastoreService ds = DatastoreServiceFactory
			.getDatastoreService();

	/** encapsulates the creation of an Entity with a special kind (defined in {@see Constants}) */
	public Entity createEntity() {
		Entity e = new Entity(_kind);
		return e;

	}

	/** Adds an Entity in the Datastore based on the request parameters.
	 * Parameters are obtained with req.getParameter, hence supports both POST and GET parameters
	 * */
	public Entity createEntity(Map<String, String> m) {
		Entity e = createEntity();
	  
		for (String propertyName : m.keySet()) {
			if (Constants.CALLBACK.equals(propertyName)) { continue; }
			String propertyValue = m.get(propertyName);
			if (propertyValue.length()<500) {
				e.setProperty(propertyName, propertyValue);}
			else {
				e.setProperty(propertyName, new Text(propertyValue));}
		}
    e.setProperty(Constants.CREATIONDATE, new Date());

		ds.put(e);
		
		return e;
	}


	/** Returns the entity corresponding to this key */
	public Entity getEntity(String key) throws Exception {    
    try {
      return ds.get(KeyFactory.createKey(_kind, Long.parseLong(key)));
    }
    catch ( EntityNotFoundException  err ) { // if the entity does not exist
      throw new NotFoundException();
    }	  	
    catch ( NullPointerException  err ) { // if key is null
      throw new NotFoundException();
    }
	}
	

	/** Returns a list of JSONObject bas on the parameters in the request */
	public List<Entity> search(Map<String,String> m) throws Exception {
		Query q = new Query(_kind);
		List<Entity> l = new ArrayList<Entity>();
		int nQueryTerms = 0;
		for (Object val : m.keySet()) {
			if (Constants.CALLBACK.equals(val)) { continue; }
	    
			// jquery adds a _ parameter if the "cache" option is set to false
			// this is not part of the query
			if ("_".equals(val)) { continue; }

			q.addFilter(val.toString(), FilterOperator.EQUAL, m.get(val));
			nQueryTerms++;
		}
		if (nQueryTerms == 0) { 
			// return an empty list
			return l;
		}
		
		// we have an index only for "title"
		if (m.containsKey("title")) {
		  q.addSort(Constants.CREATIONDATE);
		}
		
		PreparedQuery result = ds.prepare(q);
		for (Entity e: result.asIterable()) {
			l.add(e);
		}
		
		return l.subList(0, Math.min(l.size(), Constants.MAXRESULTS));
	}

	
}
